using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using pb = global::Google.Protobuf;
using Net.Messgae;

public class MsgHandler
{
    public delegate void OnMessage<T>(uint msgID,T message) where T : pb::IMessage<T>,new();

    interface IHandler
    {
        void onHandler(uint msgID,pb::ByteString data);
    }

    class Handler<T> : IHandler where T : pb::IMessage<T>,new()
    {   
        public OnMessage<T> onMessage;

        public void onHandler(uint msgID,pb::ByteString data)
        {
            var stream = data.CreateCodedInput();
            T message = new T();
            message.MergeFrom(stream);

            if(onMessage != null)
            {
                onMessage(msgID,message);
            }
        }
    }

    private byte[] sendBuffer = new byte[NetConnTCP.SendBufferSize];

    private Dictionary<uint, List< IHandler > > msgHandlers = new Dictionary<uint, List< IHandler > >();

    private NetworkManager networkManager = null;

    public MsgHandler()
    {

    }

    public void SetNetworkManager(NetworkManager manager)
    {
        networkManager = manager;
    }

    public bool SetMsgHandler<T>(MessageId id,OnMessage<T> onMessage,bool enable) where T : pb::IMessage<T>,new()
    {   
        uint msgID = (uint)id;
        
        if(!msgHandlers.ContainsKey(msgID))
        {
            msgHandlers[msgID] = new List< IHandler >();
        }

        int index = -1;

        var list = msgHandlers[msgID];

        for(int i = 0; i < list.Count; i++)
        {
            Handler<T> h = (Handler<T>)list[i];
            if(onMessage == h.onMessage)
            {
                index = i;
                break;
            }
        }

        if(enable)
        {
            if(index != -1)
            {
                return false;
            }

            var handler = new Handler<T>();
            handler.onMessage = onMessage;

            list.Add(handler);
        }
        else
        {
            if(-1 == index)
            {
                return false;
            }

            list.RemoveAt(index);
        }

        return true;
    }

    public bool SendMessage(MessageHeader header,bool isUdp = false)
    {
        var codeStream = new pb::CodedOutputStream(sendBuffer);
        header.WriteTo(codeStream);

        Debug.Log("SendMessage msg id:" + header.Id);

        if(!isUdp)
        {
            return networkManager.SendTcpMessage(sendBuffer, 0, (int)codeStream.Position);
        }
        else
        {   
            return networkManager.SendUdpMessage(sendBuffer, 0, (int)codeStream.Position);
        }
    }

    public bool SendEmptyMessage(MessageId id,bool isUdp = false)
    {
        uint msgID = (uint)id;

        MessageHeader header = new MessageHeader();
        header.Id = msgID;

        return SendMessage(header,isUdp);
    }

    public bool SendMessage(MessageId id,pb::IMessage message,bool isUdp = false)
    {
        uint msgID = (uint)id;

        MessageHeader header = new MessageHeader();
        header.Id = msgID;

        var stream = new pb::CodedOutputStream(sendBuffer);
        message.WriteTo(stream);

        header.Data = pb::ByteString.CopyFrom(sendBuffer,0,(int)stream.Position);

        return SendMessage(header,isUdp);
    }

    public void OnPacket(MessageHeader header)
    {
        if(!msgHandlers.ContainsKey(header.Id))
        {
            Debug.LogError("MsgHandler not find msg handler! msg id:" + header.Id);
            return;
        }
        
        var list = msgHandlers[header.Id];
        foreach(var handler in list)
        {
            if(handler != null)
            {
                handler.onHandler(header.Id,header.Data);
            }
        }
    }

    public void OnTcpPacket(byte[] data,int size)
    {
        var header = MessageHeader.Parser.ParseFrom(new pb::CodedInputStream(data,0,size));

        Debug.Log("OnTcpPacket id:" + header.Id);

        OnPacket(header);
    }

    public void OnUdpPacket(byte[] data,int offset,int size)
    {
        var header = MessageHeader.Parser.ParseFrom(new pb::CodedInputStream(data,offset,size));

        Debug.Log("OnUdpPacket id:" + header.Id);

        OnPacket(header);
    }
}